package demo.java;

import java.util.ArrayList;
import java.util.List;

/**
 * Java 泛型
 * <p>
 * 在java泛型中，?是java泛型中的通配符，代表未知类型,那么<? extends T>就代表类型T的某个子类,<? super T>就代表类型T的某个父类
 * <li>< ? extends Object>表示上边界限定通配符，
 * <li>< ? super Object>表示下边界限定通配符。
 * <p>
 * 
 * <h1>总结</h1>
 * <li>限定通配符总是包括自己
 * <li>上界类型通配符：add方法受限
 * <li>下界类型通配符：get方法受限
 * <li>如果你想从一个数据类型里获取数据，使用 ? extends 通配符
 * <li>如果你想把对象写入一个数据结构里，使用 ? super 通配符
 * <li>如果你既想存，又想取，那就别用通配符
 * <li>不能同时声明泛型通配符上界和下界
 *
 */
public class JavaGeneric {

    /**
     * 实例化泛型对象，我们不能够确定eList存储的数据类型是Integer还是Long，因此我们使用List<? extends Number>定义变量的类型。
     * 
     * 上界类型通配符add方法受限，但可以获取列表中的各种类型的数据，并赋值给父类型（extends Number）的引用。
     * 
     * 因此如果你想从一个数据类型里获取数据，使用 ? extends 通配符。限定通配符总是包括自己。
     */
    public void test1() {
        List<? extends Number> eList = null;
        eList = new ArrayList<Integer>();
        Number n0 = eList.get(0);
        // 语句错误
        // Integer i0 = eList.get(0);

        Number n = Integer.valueOf(1);
        // 语句错误
        // eList.add(n);
        Object o = Integer.valueOf(1);
        // 语句错误
        // eList.add(o);
        // 语句错误
        // eList.add(Integer.valueOf(1));

        eList = new ArrayList<Long>();
    }

    /**
     * 下界类型通配符get方法受限，但可以往列表中添加各种数据类型的对象。因此如果你想把对象写入一个数据结构里，使用 ? super 通配符。限定通配符总是包括自己。
     */
    public void testSuper() {
        List<? super Integer> sList = null;

        sList = new ArrayList<Number>();

        // 语句错误
        // Number numObj = sList.get(0);

        // 语句错误
        // Integer intObj = sList.get(0);

        Object obj = sList.get(0);
        Number n1 = Integer.valueOf(1);
        // 语句错误
        // sList.add(n1);

        Integer i2 = Integer.valueOf(2);
        sList.add(i2);

        sList.add(new Integer(1));
    }

    /**
     * 泛型
     *
     * @param <T1>
     *            the type of the value being boxed
     * @param <T2>
     * @param <Tn>
     */
    class Name<T1, T2, Tn> {
        private T1 t1;

        private T2 t2;

        private Tn tn;

        public <U extends Number> void inspect(U u) {
            System.out.println("T1: " + t1.getClass().getName());
            System.out.println("U: " + u.getClass().getName());
        }

        public T1 getT1() {
            return t1;
        }

        public void setT1(T1 t1) {
            this.t1 = t1;
        }

        public T2 getT2() {
            return t2;
        }

        public void setT2(T2 t2) {
            this.t2 = t2;
        }

        public Tn getTn() {
            return tn;
        }

        public void setTn(Tn tn) {
            this.tn = tn;
        }
    }
}
